#include "bcm2121.h"
#include "gpio.h"
#include "KeypadTest.h"
#include "ftuart.h"
#include "gsm.h"
#include "bcm_drv.h"
#include "udelay.h"
#include "util.h"

#define MAX_SECTOR_SIZE         0x10000     // for allocate memory
static unsigned int     sector_buff[MAX_SECTOR_SIZE];


#define FP_StartReq 0x5a        // command to request start transmission
#define FP_StartOK  0xa5        // command to answer request start transmission
#define FP_ImageOK  0x26        // Write one sector OK
#define FP_SectorOK         (FP_ImageOK+1)          // Write one sector OK
#define FP_Failed           (FP_SectorOK+1)         // Writing failed because unknown reason
#define FP_Finished         (FP_Failed+1)           // all image writing complete
#define FP_Resend           (FP_Finished+1)         // request resend last sector
#define FP_AddrWrong            (FP_Resend+1)           // Start address out of range
#define FP_ImageTooLarge        (FP_AddrWrong+1)        // Image too large compare to the flash space
#define FP_RequestBlockSize         (FP_ImageTooLarge+1)    // request the sector size
#define FP_ReturnBlockSize      (FP_RequestBlockSize+1) // retrun the sector size

#define FP_Speed_115k           (0x88)  // use 115k to download
#define FP_Speed_406k           (0x89)  // use 406k to download
#define FP_Speed_806k           (0x90)  // use 806k to download
#define FP_Speed_Req            (0x91)  // request baudrate
#define FP_Speed_ChgDone        (0x92)  // baudrate change done



void bcm2121_on2pc(void)
{
  //OSCC TOUT_EN for 32K HZ CLK bit2
  OSCC |= (0x1<<2);

  udelay(100000);

    xs_setgpio_AFR(34, GPIO_AS_AF1);
    xs_setgpio_dir(34, GPIO_PIN_IN);

    xs_setgpio_AFR(37, GPIO_AS_AF3);
    xs_setgpio_dir(37, GPIO_PIN_IN);

    xs_setgpio_AFR(26, GPIO_AS_AF3);
    xs_setgpio_dir(26, GPIO_PIN_IN);

    xs_setgpio_AFR(27, GPIO_AS_AF3);
    xs_setgpio_dir(27, GPIO_PIN_IN);

    xs_setgpio_AFR(39, GPIO_AS_GPIO);
    xs_setgpio_dir(39, GPIO_PIN_IN);

  xs_setgpio_AFR(22, GPIO_AS_GPIO);
  xs_setgpio_dir(22, GPIO_PIN_OUT);

  xs_setgpio_outlow(22);

  //power on broadcom module
  xs_setgpio_AFR(25, GPIO_AS_GPIO);
  xs_setgpio_dir(25, GPIO_PIN_OUT);

  xs_setgpio_outhigh(25);

  udelay(100000);

  xs_setgpio_AFR(23, GPIO_AS_GPIO);
  xs_setgpio_dir(23, GPIO_PIN_OUT);

  xs_setgpio_outlow(23);

  udelay(300000);

  xs_setgpio_outhigh(23);

  //Wake up BCM2121 for the first time
  xs_setgpio_outlow(114);
  udelay(100000);
  xs_setgpio_outhigh(114);


    return;
}

void bcm2121_download2pc(void)
{
    xs_setgpio_AFR(34, GPIO_AS_AF1);
    xs_setgpio_dir(34, GPIO_PIN_IN);

    xs_setgpio_AFR(37, GPIO_AS_AF3);
    xs_setgpio_dir(37, GPIO_PIN_IN);

    xs_setgpio_AFR(26, GPIO_AS_AF3);
    xs_setgpio_dir(26, GPIO_PIN_IN);

    xs_setgpio_AFR(27, GPIO_AS_AF3);
    xs_setgpio_dir(27, GPIO_PIN_IN);

    xs_setgpio_AFR(39, GPIO_AS_GPIO);
    xs_setgpio_dir(39, GPIO_PIN_IN);

    xs_setgpio_AFR(22, GPIO_AS_GPIO);
    xs_setgpio_dir(22, GPIO_PIN_OUT);

    xs_setgpio_outhigh(22);

    xs_setgpio_AFR(23, GPIO_AS_GPIO);
    xs_setgpio_dir(23, GPIO_PIN_OUT);

    xs_setgpio_outlow(23);


        //power on broadcom module
    xs_setgpio_AFR(25, GPIO_AS_GPIO);
    xs_setgpio_dir(25, GPIO_PIN_OUT);

    xs_setgpio_outhigh(25);

    xs_setgpio_AFR(23, GPIO_AS_GPIO);
    xs_setgpio_dir(23, GPIO_PIN_OUT);

    udelay(100);


    xs_setgpio_outhigh(23);

    return;
}


static unsigned char GetCheckSum(unsigned char  *buff,unsigned int  sector_size)
{
    unsigned int    i;
    unsigned char   check_sum;

    check_sum = 0;
    for( i=0; i<sector_size; i++ ){
        check_sum += buff[i];
    }
    return( check_sum );
}

#define Image_Start  0xa0000000;
unsigned int i_offset = 0;

static unsigned int  GetNextSector(unsigned char *buff,unsigned int sector_size)
{
    unsigned int    i;
    char* Imagebuff = (char *)(0xa0000000 + i_offset);
    // clear and read
    for( i=0; i<sector_size; i++ ){
        buff[i] = *(Imagebuff + i);
    }
    i_offset += sector_size;
    return( i_offset);

}


static void SendCurrentSectorAndCheckSum(unsigned char  *buff,unsigned int sector_size)
{
    unsigned int    i;
    unsigned int    j;

    // send whole sector
//  printk( "a new block\n " );

    for( i=0,j=0; i<sector_size; i++,j++ ){
        ftuart_output_rawbyte(buff[i] );
#if 0
        if((i%16)==0)
                {
                        printk("\n\r");
                        printk("%2x",buff[i]);
                }
                else
                {
                        printk("%2x",buff[i]);
                }
#endif
        // spin the cursor
        j &= 0x3fff;
        switch ( j ){
            case 0x000:
                printk( "\b|" );
                break;
            case 0x1000:
                printk( "\b/" );
                break;
            case 0x2000:
                printk( "\b-" );
                break;
            case 0x3000:
                printk( "\b\\" );
                break;
        }
    }
    printk( "\b" );
    printk( "P" );
    printk( "\b" );
    // send check sum
    ftuart_output_rawbyte( GetCheckSum( buff, sector_size ) );
}


int  bcm2121_imagedownload(unsigned int sa, unsigned int is, int speed)
{
    unsigned char ch,ch0,ch1,ch2,ch3;
    int error_status,ret_flag,ss = 0;
    int i;

#if 0
    int j;
    for(j=0; j< is;j++)
        {
            if((j%16)==0)
                {
                    printk("\n\r");
                        printk("%2x",*(unsigned char*)(0xa0000000 +j));
                }
                else
                {
                        printk("%2x",*(unsigned char*)(0xa0000000 +j));
                }
        }
#endif

    ftuart_gpio_init();
    udelay(100);
    ftuart_init(baud115k2);
    udelay(100);
    bcm2121_gpio_init();
    udelay(100);
    bcm2121_download_enable();
    udelay(100);
    bcm2121_on();
    udelay(1000);

    int length = sizeof(bcm_flashdrv);
    printk("bcm_flashdrv length=%d",length);
    for(i=0; i< length; i++)
    {
        ftuart_output_rawbyte( bcm_flashdrv[i]);
#if 0
        if((i%16)==0)
        {
            printk("\n\r");
            printk("%2x",bcm_flashdrv[i]);
        }
        else
        {
            printk("%2x",bcm_flashdrv[i]);
        }
#endif

    }

    udelay(1000);
    while(!ftuart_input_byte( (char *)&ch));

    if( ch == FP_Speed_Req )
    {
        printk( "\nRECEIVE <- [ Baudrate request ]" );
    }
    else
    {
        printk( "\nOut of sync ch = %d\n",ch );
        return 1;
    }

    switch( speed )
    {
        case 115:
            ch = FP_Speed_115k;
            printk( "\nSEND ----> [ Baudrate = 115k ]" );
            break;

        case 406:
            ch = FP_Speed_406k;
            printk( "\nSEND ----> [ Baudrate = 406k ]" );
            break;

        case 806:
            ch = FP_Speed_806k;
            printk( "\nSEND ----> [ Baudrate = 806k ]" );
            break;


        default:
            printk( "\nWrong Baudrate");
            return 1;
            break;
    }

    ftuart_output_rawbyte( ch );

    udelay(100);
    ftuart_input_byte((char *)&ch);

    if( ch == FP_Speed_ChgDone )
    {
        printk( "\nRECEIVE <- [ Baudrate Change Done ]" );
    }
    else
    {
            printk( "\nOut of sync" );
            return 1;
    }


    ftuart_output_rawbyte( (unsigned char)FP_StartReq );

    error_status = 0;
    ret_flag = 0;

    while(ret_flag == 0 ){

        ret_flag = 0;
        // receive command
        udelay(100);
        ftuart_input_byte( (char *)&ch );

        // switch command
        switch( ch ){
            case FP_StartOK:
                printk( "\nRECEIVE <- [ OK to Start ]" );
                printk( "\nSEND ----> [ Start_address=%08x,  Image_Size=%08x ]",sa,is );
                // send start address
                ftuart_output_rawbyte( (unsigned char) ((sa & 0xff000000) >> 24) );
                ftuart_output_rawbyte( (unsigned char) ((sa & 0x00ff0000) >> 16 ));
                ftuart_output_rawbyte( (unsigned char) ((sa & 0x0000ff00) >> 8 ) );
                ftuart_output_rawbyte( (unsigned char) (sa & 0x000000ff)  );
                // send image size
                ftuart_output_rawbyte( (unsigned char) ((is & 0xff000000) >> 24) );
                                ftuart_output_rawbyte( (unsigned char) ((is & 0x00ff0000) >> 16 ));
                                ftuart_output_rawbyte( (unsigned char) ((is & 0x0000ff00) >> 8 ) );
                                ftuart_output_rawbyte( (unsigned char) (is & 0x000000ff)  );
                break;

            case FP_ImageOK:
                printk( "\nRECEIVE <- [ Image OK! ]" );
                printk( "\nSending " );
                // request sector size
                ftuart_output_rawbyte( (unsigned char)  FP_RequestBlockSize );
                break;

            case FP_AddrWrong:
                printk( "\nRECEIVE <- [ Address Out of Range! ]" );
                error_status = 1;
                ret_flag = 1;
                i_offset = 0;
                break;

            case FP_ImageTooLarge:
                printk( "\nRECEIVE <- [ Image Too Large! ]" );
                error_status = 1;
                ret_flag = 1;
                i_offset = 0;
                break;

            case FP_ReturnBlockSize:
                ftuart_input_byte((char *) &ch3 );
                ftuart_input_byte((char *) &ch2 );
                ftuart_input_byte((char *) &ch1 );
                ftuart_input_byte((char *) &ch0 );
                ss = (ch3 << 24) | (ch2 << 16) | (ch1 << 8) | (ch0);
                // read sector
                if( GetNextSector( (unsigned char*)sector_buff, ss ) == 0 ){
                    printk( "\nSector read failed!" );
                    error_status = 1;
                    ret_flag = 1;
                    i_offset = 0;
                }else{
                    // send current sector
                    SendCurrentSectorAndCheckSum( (unsigned char*)sector_buff, ss );
                }
                break;

            case FP_SectorOK:
                printk( "." );
                // request sector size
                ftuart_output_rawbyte( (unsigned char)FP_RequestBlockSize );
                break;

            case FP_Resend:
                // resend current sector
                printk( "\nRECEIVE <- [ Resend request ]" );
                SendCurrentSectorAndCheckSum( (unsigned char*)sector_buff, ss );
                break;

            case FP_Failed:
                printk( "\nRECEIVE <- [ Transmission Failed ]" );
                error_status = 1;
                ret_flag = 1;
                i_offset = 0;
                break;

            case FP_Finished:
                printk( "\nRECEIVE <- [ Transmission Completely Done ]\n" );
                error_status = 0;
                ret_flag = 1;
                i_offset = 0;
                break;

            default:
                printk( "\nFailed by Unknown Reason( debug_code:%02x ) !\n", ch );
                error_status = 1;
                ret_flag = TRUE;
                i_offset = 0;
                break;
        }


    }
    printk("bcm2121 off\n");
    bcm2121_download_disable();
    bcm2121_off();
    i_offset = 0;
    return error_status;

}


void bcm2121_active(void)
{
  GPSR3 = 0x1 << 18;
  return;
}

void bcm2121_inactive(void)
{
  GPCR3 = 0x1 << 18;
  return;
}
